<?php
define('IN_SYS', true);

define('DS', DIRECTORY_SEPARATOR);

define('SYS_ROOT', dirname(dirname(dirname(__FILE__))) . DS);

define('NOROBOT', TRUE);

define('INCLUDE_DIR', SYS_ROOT . 'include' . DS);

define('DATA_DIR', SYS_ROOT . 'sitedata' . DS);

define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());

define('APPPATH', realpath($application).DS);

define('MODPATH', realpath($modules).DS);

define('SYSPATH', realpath($system).DS);

define('DATAPATH', realpath($data).DS);

if (!defined('IN_SYS')) exit('Access Denied!');

require_once(INCLUDE_DIR . 'global.inc.php');

/**
 * 核心类文件
 *
 * @author Jerry
 */
class Co
{
    /**
     * @var  string  cache directory
     */
    public static $cache_dir;

    /**
     * @var  string  config directory
     */
    public static $config_dir;

    /**
     * @var  string  debug type
     */
    public static $debug;

     /**
     * @var  string  time zone
     */
    public static $time_zone;

    /**
	 * @var  object  logging object
	 */
	public static $log;

	/**
	 * @var  boolean  command line environment?
	 */
	public static $is_cli = FALSE;

	/**
	 * @var  string  character set of input and output
	 */
	public static $charset = 'utf-8';

	/**
	 * @var  boolean  enabling internal caching?
	 */
	public static $caching = FALSE;

	/**
	 * @var  boolean  enable error handling?
	 */
	public static $errors = TRUE;

	/**
	 * @var  array  types of errors to display at shutdown
	 */
	public static $shutdown_errors = array(E_PARSE, E_ERROR, E_USER_ERROR, E_COMPILE_ERROR);

     /**
     * @var  string  init system
     */
    static public function init()
    {
        //error_reporting(self::$debug);

        spl_autoload_register(array('Co', 'autoload'));

        self::$cache_dir = DATA_DIR . 'cache';

        self::$config_dir = INCLUDE_DIR . 'Configs';

        ini_set("magic_quotes_runtime", 0);

        date_default_timezone_set(self::$time_zone);

        $starttime = microtime(true);

        mb_internal_encoding(Co::$charset);
		
        if (self::$errors) {
        	// 开启错误缓冲放弃其他的debug
	        ob_start();
	    	// Enable Co exception handling, adds stack traces and error source.
			set_exception_handler(array('Co', 'exception_handler'));
			// Enable Co error handling, converts all PHP errors to exceptions.
			set_error_handler(array('Co', 'error_handler'));
			// Enable the Co shutdown handler, which catches E_FATAL errors.
			register_shutdown_function(array('Co', 'shutdown_handler'));
        }
    }

    public static $php_errors = array(
		E_ERROR              => 'Fatal Error',
		E_USER_ERROR         => 'User Error',
		E_PARSE              => 'Parse Error',
		E_WARNING            => 'Warning',
		E_USER_WARNING       => 'User Warning',
		E_STRICT             => 'Strict',
		E_NOTICE             => 'Notice',
		E_RECOVERABLE_ERROR  => 'Recoverable Error',
	);

    static public function Config($key = 'Sys', $data = NULL)
    {
        /* config file is a hash of the name */
        $config_file = self::$config_dir .DS. ucfirst(strtolower($key)) . '.config.ini';
        try
        {
            if ($data === NULL)
            {
                if (is_file($config_file))
                {
                    return unserialize(file_get_contents($config_file));
                }
                return NULL;
            }
            else
            {
                if (!is_dir(self::$config_dir))
                {
                    @mkdir(self::$config_dir, 0777, TRUE);
                    @chmod(self::$config_dir, 0777);
                }
                if (!is_writable(self::$config_dir))
                {
                	throw new Exception('Directory :'.self::$config_dir.'dir must be writable');
                }

                $_CONFIGS = self::Config($key);
                if (is_array($data))
                {
                    foreach ($data as $k => $d)
                    {
                        $_CONFIGS[$k] = $d;
                    }
                }
                return (bool) file_put_contents($config_file, serialize($_CONFIGS));
            }
        }
        catch (Exception $e)
        {
            throw $e;
        }
    }
	
    /**
     * 自动加载 include / Classes 中的类文件
     *
     * @param string $class 类名
     * @return boolean
     */
	public static function autoload ($class)
    {
        if (class_exists($class, FALSE)) return TRUE;
		$file = INCLUDE_DIR . 'Classes' .DS. ucfirst(strtolower($class) .'.class.php');
		if (file_exists($file)) {
			require_once $file;
            return TRUE;
		}
		return FALSE;
    }
	
    /**
     * 手动加载类方法
     *
     * @param string $file 文件路径
     * @return boolean
     */
    static public function Load($file)
    {
        return include_once $file;
    }

    static public function Cache($name, $data = NULL, $lifetime = 3600)
    {
        /* Cache file is a hash of the name */
        $file = sha1($name);
        /* Cache directories are split by keys to prevent filesystem overload */
        $dir = self::$cache_dir . DS . "{$file[0]}{$file[1]}" . DS;
        try
        {
            if ($data === NULL)
            {
                if (is_file($dir . $file))
                {
                    if ((time() - filemtime($dir . $file)) < $lifetime)
                    {
                        return unserialize(file_get_contents($dir . $file));
                    }
                    else
                    {
                        @unlink($dir . $file);
                    }
                }
                return NULL;
            }
            if (!is_dir($dir))
            {
                @mkdir($dir, 0777, TRUE);
                @chmod($dir, 0777);
            }
            if (!is_writable($dir))
            {
            	throw new Exception('Directory :'.self::$cache_dir.'dir must be writable');
            }
            return (bool) file_put_contents($dir . $file, serialize($data));
        }
        catch (Exception $e)
        {
            throw $e;
        }
    }

    /**
	 * Inline exception handler, displays the error message, source of the
	 * exception, and the stack trace of the error.
	 *
	 * @uses    Co::exception_text
	 * @param   object   exception object
	 * @return  boolean
	 */
	public static function exception_handler(Exception $e)
	{
		try
		{
			// Get the exception information
			$type    = get_class($e);
			$code    = $e->getCode();
			$message = $e->getMessage();
			$file    = $e->getFile();
			$line    = $e->getLine();
			// Create a text version of the exception
			$error = Co::exception_text($e);

			if (is_object(Co::$log))
			{
				// Add this exception to the log
				Co::$log->add(Co::ERROR, $error);

				// Make sure the logs are written
				Co::$log->write();
			}

			if (Co::$is_cli)
			{
				// Just display the text of the exception
				echo "\n{$error}\n";

				return TRUE;
			}

			// Get the exception backtrace
			$trace = $e->getTrace();

			if ($e instanceof ErrorException)
			{
				if (isset(Co::$php_errors[$code]))
				{
					// Use the human-readable error name
					$code = Co::$php_errors[$code];
				}

				if (version_compare(PHP_VERSION, '5.3', '<'))
				{
					// Workaround for a bug in ErrorException::getTrace() that exists in
					// all PHP 5.2 versions. @see http://bugs.php.net/bug.php?id=45895
					for ($i = count($trace) - 1; $i > 0; --$i)
					{
						if (isset($trace[$i - 1]['args']))
						{
							// Re-position the args
							$trace[$i]['args'] = $trace[$i - 1]['args'];

							// Remove the args
							unset($trace[$i - 1]['args']);
						}
					}
				}
			}

			if ( ! headers_sent())
			{
				// Make sure the proper content type is sent with a 500 status
				header('Content-Type: text/html; charset='.Co::$charset, TRUE, 500);
			}

			// Start an output buffer
			ob_start();

			// Include the exception HTML
			include_once(INCLUDE_DIR . 'Templates' .DS. 'error.php');
//			include Co::find_file('views', Co::$error_view);

			// Display the contents of the output buffer
			echo ob_get_clean();

			return TRUE;
		}
		catch (Exception $e)
		{
			// Clean the output buffer if one exists
			ob_get_level() and ob_clean();

			// Display the exception text
			echo Co::exception_text($e), "\n";

			// Exit with an error status
			// exit(1);
		}
	}

    /**
	 * PHP error handler, converts all errors into ErrorExceptions. This handler
	 * respects error_reporting settings.
	 *
	 * @throws  ErrorException
	 * @return  TRUE
	 */
	public static function error_handler($code, $error, $file = NULL, $line = NULL)
	{
		if (error_reporting() & $code)
		{
			// This error is not suppressed by current error reporting settings
			// Convert the error into an ErrorException
			throw new ErrorException($error, $code, 0, $file, $line);
		}

		// Do not execute the PHP error handler
		return TRUE;
	}

	/**
	 * Catches errors that are not caught by the error handler, such as E_PARSE.
	 *
	 * @uses    Co::exception_handler
	 * @return  void
	 */
	public static function shutdown_handler()
	{
		if (Co::$errors AND $error = error_get_last() AND in_array($error['type'], Co::$shutdown_errors))
		{

			// Clean the output buffer
			ob_get_level() and ob_clean();

			// Fake an exception for nice debugging
			Co::exception_handler(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));

			// Shutdown now to avoid a "death loop"
			exit(0);
		}
	}

	/**
	 * Removes application, system, modpath, or docroot from a filename,
	 * replacing them with the plain text equivalents. Useful for debugging
	 * when you want to display a shorter path.
	 *
	 *     // Displays SYSPATH/classes/Co.php
	 *     echo Co::debug_path(Co::find_file('classes', 'Co'));
	 *
	 * @param   string  path to debug
	 * @return  string
	 */
	public static function debug_path($file)
	{
		if (strpos($file, APP_DIR) === 0)
		{
			$file = APP_DIR.substr($file, strlen(APP_DIR));
		}
		elseif (strpos($file, SYSPATH) === 0)
		{
			$file = SYSPATH.substr($file, strlen(SYSPATH));
		}
		elseif (strpos($file, MODPATH) === 0)
		{
			$file = MODPATH.substr($file, strlen(MODPATH));
		}
		elseif (strpos($file, SYS_ROOT) === 0)
		{
			$file = DOCROOT.substr($file, strlen(DOCROOT));
		}
		return $file;
	}

	/**
	 * Get a single line of text representing the exception:
	 *
	 * Error [ Code ]: Message ~ File [ Line ]
	 *
	 * @param   object  Exception
	 * @return  string
	 */
	public static function exception_text(Exception $e)
	{
		return sprintf('%s [ %s ]: %s ~ %s [ %d ]', get_class($e), $e->getCode(), strip_tags($e->getMessage()), Co::debug_path($e->getFile()), $e->getLine());
	}

		/**
	 * Returns an HTML string, highlighting a specific line of a file, with some
	 * number of lines padded above and below.
	 *
	 *     // Highlights the current line of the current file
	 *     echo Co::debug_source(__FILE__, __LINE__);
	 *
	 * @param   string   file to open
	 * @param   integer  line number to highlight
	 * @param   integer  number of padding lines
	 * @return  string   source of file
	 * @return  FALSE    file is unreadable
	 */
	public static function debug_source($file, $line_number, $padding = 5)
	{
		if ( ! $file OR ! is_readable($file))
		{
			// Continuing will cause errors
			return FALSE;
		}

		// Open the file and set the line position
		$file = fopen($file, 'r');
		$line = 0;

		// Set the reading range
		$range = array('start' => $line_number - $padding, 'end' => $line_number + $padding);

		// Set the zero-padding amount for line numbers
		$format = '% '.strlen($range['end']).'d';

		$source = '';
		while (($row = fgets($file)) !== FALSE)
		{
			// Increment the line number
			if (++$line > $range['end'])
				break;

			if ($line >= $range['start'])
			{
				// Make the row safe for output
				$row = htmlspecialchars($row, ENT_NOQUOTES, Co::$charset);

				// Trim whitespace and sanitize the row
				$row = '<span class="number">'.sprintf($format, $line).'</span> '.$row;

				if ($line === $line_number)
				{
					// Apply highlighting to this row
					$row = '<span class="line highlight">'.$row.'</span>';
				}
				else
				{
					$row = '<span class="line">'.$row.'</span>';
				}

				// Add to the captured source
				$source .= $row;
			}
		}

		// Close the file
		fclose($file);

		return '<pre class="source"><code>'.$source.'</code></pre>';
	}

	/**
	 * Returns an array of HTML strings that represent each step in the backtrace.
	 *
	 *     // Displays the entire current backtrace
	 *     echo implode('<br/>', Co::trace());
	 *
	 * @param   string  path to debug
	 * @return  string
	 */
	public static function trace(array $trace = NULL)
	{
		if ($trace === NULL)
		{
			// Start a new trace
			$trace = debug_backtrace();
		}

		// Non-standard function calls
		$statements = array('include', 'include_once', 'require', 'require_once');

		$output = array();
		foreach ($trace as $step)
		{
			if ( ! isset($step['function']))
			{
				// Invalid trace step
				continue;
			}

			if (isset($step['file']) AND isset($step['line']))
			{
				// Include the source of this step
				$source = Co::debug_source($step['file'], $step['line']);
			}

			if (isset($step['file']))
			{
				$file = $step['file'];

				if (isset($step['line']))
				{
					$line = $step['line'];
				}
			}

			// function()
			$function = $step['function'];

			if (in_array($step['function'], $statements))
			{
				if (empty($step['args']))
				{
					// No arguments
					$args = array();
				}
				else
				{
					// Sanitize the file path
					$args = array($step['args'][0]);
				}
			}
			elseif (isset($step['args']))
			{
				if ( ! function_exists($step['function']) OR strpos($step['function'], '{closure}') !== FALSE)
				{
					// Introspection on closures or language constructs in a stack trace is impossible
					$params = NULL;
				}
				else
				{
					if (isset($step['class']))
					{
						if (method_exists($step['class'], $step['function']))
						{
							$reflection = new ReflectionMethod($step['class'], $step['function']);
						}
						else
						{
							$reflection = new ReflectionMethod($step['class'], '__call');
						}
					}
					else
					{
						$reflection = new ReflectionFunction($step['function']);
					}

					// Get the function parameters
					$params = $reflection->getParameters();
				}

				$args = array();

				foreach ($step['args'] as $i => $arg)
				{
					if (isset($params[$i]))
					{
						// Assign the argument by the parameter name
						$args[$params[$i]->name] = $arg;
					}
					else
					{
						// Assign the argument by number
						$args[$i] = $arg;
					}
				}
			}

			if (isset($step['class']))
			{
				// Class->method() or Class::method()
				$function = $step['class'].$step['type'].$step['function'];
			}

			$output[] = array(
				'function' => $function,
				'args'     => isset($args)   ? $args : NULL,
				'file'     => isset($file)   ? $file : NULL,
				'line'     => isset($line)   ? $line : NULL,
				'source'   => isset($source) ? $source : NULL,
			);

			unset($function, $args, $file, $line, $source);
		}

		return $output;
	}

}

/**
 * Co exception class. Translates exceptions using the [I18n] class.
 *
 * @package    Co
 * @category   Exceptions
 * @author     Co Team
 * @copyright  (c) 2008-2009 Co Team
 * @license    http://Cophp.com/license
 */
class FwException extends Exception {

	/**
	 * Creates a new translated exception.
	 *
	 *     throw new CoException('Something went terrible wrong, :user',
	 *         array(':user' => $user));
	 *
	 * @param   string     error message
	 * @param   array      translation variables
	 * @param   integer    the exception code
	 * @return  void
	 */
	public function __construct($message, array $variables = NULL, $code = 0)
	{
		// Set the message
		$message = strtr($message, $variables);

		// Pass the message to the parent
		parent::__construct($message, $code);
	}

	/**
	 * Magic object-to-string method.
	 *
	 *     echo $exception;
	 *
	 * @uses    Co::exception_text
	 * @return  string
	 */
	public function __toString()
	{
		return Co::exception_text($this);
	}

} // End CoException
?>